# -*- coding: utf-8 -*-
""" Provides web-application handling routines (exports in PSP as pdcs) """

from time import time
from mod_python import apache, psp, util, Cookie
from urllib2 import urlopen, unquote
import cPickle
import os, gc, re
import ProfitDCS as pdcs_fcn
import ProfitDCS.db as db
import ProfitDCS.ml.xml as xml
import ProfitDCS.ml.sgml as sgml
from ProfitDCS import concat_keys, notemptylist, pdcs_version
from ProfitDCS.security import md5sum
from logging import warning as w

__all__ = ['web']
__author__ = 'Sergey Sobko, Timir Nureev'
__copyright__ = 'Copyright (c) 2010, DEKO PROFIT (The ProfitWare Project)'
__license__ = 'BSD'
__credits__ = ['Sergey Sobko', 'Timir Nureev']
__email__ = 'info@dekoprofit.ru'
__status__ = 'Development'

class web:
	version = pdcs_version()
	templateused = False
	__options = {}
	webfile = ''
	usepage = ''
	project = None
	projdata = {}
	styles = []
	scripts = []
	nav = {}
	dojocfg = ''
	databases = {}
	uservalue = {}
	rules = []
	useractions = []
	__secret = 'ei4f3098qh048hfa3098hd043f435t4t'
	roles = 0
	uid = 0
	login = 'anonymous'
	pwd = ''
	name = 'Anonymous'
	surname = ''
	pagetitle = ''
	description = ''
	keywords = ''
	title = ''
	navid = 'default'
	needrole = 0
	strings = {}
	images = {'default': {}}
	userprofile = {}
	tags = {}
	__db = {}
	dbs = None
	config = None
	host = ''
	pstart = None
	
	dcs_fcn = pdcs_fcn
	
	NOT_FOUND = apache.HTTP_NOT_FOUND
	md5 = lambda self, x: md5sum(x)
	unquote_url = lambda self, x: unquote(x)

	def __init__(self, req, xmlconfig = '', fileroot = '', preinit = None):
		self.pstart = time()
		self.req = req
		self.__options.update({'path': fileroot})
		self.projtitle = os.path.realpath('.')
		self.form = util.FieldStorage(req)
		uri = self.req.uri
		self.host = self.req.headers_in.has_key('Host') and self.req.headers_in['Host'] or ''
		self.uservalue = {}
		self.webfile = uri[uri.rfind('/')+1:]
		self.usepage = self.webfile
		if os.path.exists(xmlconfig):		
			self.parse_xmlconfig(xmlconfig)
		if preinit is not None and type(preinit).__name__ == 'function':
			self = preinit(self)
#		if self.webfile == 'handler.py' or self.webfile.split('/')[-1] == xmlconfig:
	
	path = lambda self: self.__options['path']
	def unauth(self):
		w('ProfitDCSWeb / Unauth error: User = %s (%s), Host = %s, URI = %s' % (self.name + ' ' + self.surname, self.uid, self.host, self.req.uri))
		raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN
		
	def parse_xmlconfig(self, xmlconfig):
		x = xml.xml_parser(open(xmlconfig, 'r').read())
		self.config = x
		self.projdata = x.get_element('/project')[0].attr()
		if self.projdata.has_key('secret'):
			self.__secret = self.projdata['secret']
		[self.__options.update({i.attr('id'): i.attr('value')}) for i in x.get_element('/project/option')]

		try:
			self.tags = {}
			for i in x.get_element('/project/head/nav'):
				navid = i.attr('id') is None and 'default' or i.attr('id')
				self.nav[navid] = []
				for n in x.get_element('/project/head/nav[id=%s]/item' % navid):
					href, title, roles, bbcode, pagetitle, keywords, description, usepage, tags = map(n.attr, ['href', 'title', 'roles', 'bbcode', 'pagetitle', 'keywords', 'description', 'usepage', 'tags'])
					if type(tags).__name__ == 'str':
						for p in tags.split(', '):
							if self.tags.has_key(p):
								self.tags[p] += [href]
							else:
								self.tags.update({p: [href]})
					bbcode = type(bbcode).__name__ == 'str' and bbcode == 'true'
					roles = type(roles).__name__ == 'str' and int(roles) or 0
					self.nav[navid] += [(href, title, roles, bbcode)]
					if href.find(self.webfile) > -1:
						self.usepage = (type(usepage).__name__ == 'str') and usepage or self.webfile
						self.navid = navid
						self.pagetitle, self.needrole = (title, roles,)
						self.title = (pagetitle is None) and title or pagetitle
						self.keywords = type(keywords).__name__ == 'str' and keywords or self.keywords
						self.description = type(description).__name__ == 'str' and description or self.description
		except Exception, e:
			w('ProfitDCSWeb / Error reading configuration: %s' % e)
			pass

		if self.__options.has_key('pickle_file'):
			try:
				self.strings, self.databases, self.images = cPickle.load(self.__options['pickle_file'])
				return
			except Exception, e:
				w('ProfitDCSWeb / Loading Pickle failed: %s' % e)
				pass

		[self.strings.update({i.attr('id'): i.attr('value')}) for i in x.get_element('/project/head/string')]
		for img in x.get_element('/project/head/images'):
			catid = img.attr('catid') is None and 'default' or img.attr('catid')
			if not self.images.has_key(catid):
				self.images.update({ catid: {}})
			for i in x.get_element('/project/head/images[catid=%s]/image' % catid):
				self.images[catid].update({i.attr('id'): i.attr('value')})
		self.dbs = db.databases(x)
		globals().update(self.dbs)
		self.databases = self.dbs.databases	
		

		try:
			cPickle.dump((self.strings, self.databases, self.images), self.__options['pickle_file'])
		except Exception, e:
			w('ProfitDCSWeb / Saving Pickle failed: %s' % e)
			pass

		return

	def set_uv(self, key, value):
		self.uservalue.update({key: value})

	def get_uv(self, key):
		if not self.uservalue.has_key(key):
			return ''
		return self.uservalue[key]
		
	def rewrite_by_rules(self, rule):			
		return
		
	profitdcs_info = lambda self: self.templateused and self.req.write('Powered by <a href="http://profitware.ru/">ProfitDCS</a> %(version)s &copy; 2010, <a href="http://dekoprofit.ru/">DEKO PROFIT</a>. Page generated in %(gen)f sec. with %(qnum)d queries (%(qc)d cached).' % {'version': self.version, 'gen': time() - self.pstart, 'qnum': self.dbs.qnum, 'qc': self.dbs.qc}) or ''
		
	def redirect(self, redir=''):
		if redir == '':
			redir = self.webfile
		util.redirect(self.req, redir)
		
	def check_file(self, filename):
		try:
			urlopen(filename)
		except Exception:
			return False
		return True
	
	check_local_file = lambda self, x: os.access(self.__options['path'] + x, os.F_OK)
	
	check_key = lambda self, ckey: self.form.has_key(ckey)
	check_keys = lambda self, ckeys: notemptylist(ckeys) and not (False in map(self.check_key, ckeys))
	get_key = lambda self, ckey, ifno='': self.check_key(ckey) and self.form[ckey] or ifno

	def run_template(self, template_file, keys={}):
		try:
			pth = self.__options['path']
		except Exception:
			w('ProfitDCSWeb / Local path FAIL: %s' % e)
			pth = ''
		self.req.content_type = 'text/html'
		for i in self.nav.values():
			for x in i:
				if (x[0] == template_file) and (x[3] == True):
					self.req.write(self.html_bbcode(sgml.html_escape(open(pth + template_file, 'r').read())))
					return
		self.template = psp.PSP(self.req, filename=template_file)
		keys.update({'pdcs': self, 'pdcs_fcn': pdcs_fcn})
		keys.update(self.dbs)
		self.templateused = True
		self.template.run(keys)

	def get_cookie(self, name):
		marshal_cookies = Cookie.get_cookies(self.req, Cookie.MarshalCookie, secret=self.__secret)
		returned_marshal = marshal_cookies.get(name, None)
		return ((returned_marshal and type(returned_marshal) is Cookie.MarshalCookie) and returned_marshal.value or {})

	def set_cookie(self, name, dct, days = 30):
		send_marshal = Cookie.MarshalCookie(name, dct, self.__secret)
		send_marshal.expires = time() + days * 24 * 60 * 60
		Cookie.add_cookie(self.req, send_marshal)
		return None
	
	def restore_auth_cookie(self, dbid, qid='auth'):
		c = self.get_cookie('pdcs_auth')
		return ((c.has_key('login') and c.has_key('pass')) and self.check_auth_db(c['login'], c['pass'], dbid, qid) or self.check_auth_db('anonymous', '', dbid, qid))

	def check_auth_db(self, login, pwd, dbid, qid='auth'):
		V = self.dbs.sql_query(dbid, qid, (pwd, login, md5sum(pwd),), lambda x: x)
		return (len(V) == 1) and V[0] or None

	send_auth_cookie = lambda self, login, pwd, days = 30: self.set_cookie('pdcs_auth', {'login': login, 'pass': pwd}, days)
	
	inrole = lambda self, role: self.roles & role == role
					
	def ret_status(self):
		for dbdict in self.__db.values():
			dbdict['conn'].commit()
			dbdict['conn'].close()
			del dbdict['conn']
		del self.uservalue
		gc.collect()
		return apache.OK
	
	html_escape = lambda self, x: sgml.html_escape(x)
	
	def html_bbcode(self, s, imgattrs = {}):
		def imgreplace(x):
			sp = x.split('/')
			catid = sp[0]
			id = '/'.join(sp[1:])
			style = ''
			if imgattrs.has_key('style'):
				style = 'style="%s" ' % imgattrs['style']
			try:
				return '<img src="' + self.images[catid][id] + '" alt="' + catid + ' / ' + id + '" id="img_' + catid + '_' + id + '" ' + style + '/>'
			except Exception:
				pass
			try:
				dbid, qid = imgattrs['dbid'], imgattrs['qid']
				urlx = self.dbs.sql_query(dbid, qid, (catid, id,), lambda x: x[0][0])	
				if self.check_file(urlx) or imgattrs.has_key('nocheck'):
					return '<img src="%(url)s" alt="%(catid)s / %(id)s" id="img_%(catid)s_%(id)s" %(style)s/>' % {'url': urlx, 'catid': catid, 'id': id, 'style': style} 
			except Exception:
				pass
			try:
				if self.check_file(imgattrs['url'] % {'catid': catid, 'id': id}) or imgattrs.has_key('nocheck'):
					return '<img src="%(url)s" alt="%(catid)s / %(id)s" id="img_%(catid)s_%(id)s" %(style)s/>' % {'url': imgattrs['url'] % {'catid': catid, 'id': id}, 'catid': catid, 'id': id, 'style': style} 
			except Exception:
				pass
			return ''
		s = s.replace("[b]", "<b>")
		s = s.replace("[i]", "<i>")
		s = s.replace("[/b]", "</b>")
		s = s.replace("[/i]", "</i>")
		s = s.replace("[br]", "<br>")
		try:
			s =  re.sub('\[img (.*?/.*?)\]', lambda x: imgreplace(x.group(1)) , s)
		except Exception:
			pass
		return s

	img = lambda self, x, imgattrs = {}: self.html_bbcode('[img %s]' % x, imgattrs)
	def pdcs_html_news(self, newsdata = (), ntmpl = '<h3><a href="%s">%s</a> [%s]</h3><p>%s</p>'):
		newsall = ''
		for x in newsdata:
			newsall += ntmpl % tuple(map(lambda t: self.html_bbcode(type(t).__name__ == 'str' and t or str(t)), x))			
		return newsall

	def pdcs_html_print(self, dbid, qid, data = '', title = 'Printing form'):
		def printtable(fetched):
			for i in fetched:
				self.req.write('<tr>')
				for x in i:
					if type(x).__name__ != 'str':
						x = str(x)
					if x == '':
						x = '&nbsp;'
					self.req.write('<td style="border-style: solid; border-width: 1px;">' + x + '</td>\n')
				self.req.write('</tr>')

		self.req.write('<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>')
		self.req.write(title)
		self.req.write('</title></head><body><center><h1><u>' + title + '</u></h1><br /><table style="border-style: solid; border-width: 1px; width: 100%;"><tr>\n')
		for i in self.databases[dbid]['fields_' + qid]:
			self.req.write('<td style="border-style: solid; border-width: 1px;">' + i + '</td>')
		self.req.write('</tr>')
		self.dbs.sql_query(dbid, qid, data, printtable)				
		self.req.write('</table><br /><hr />\n') 
		self.templateused = True
		self.profitdcs_info()
		self.templateused = False
		self.req.write('</center></body></html>')

	def pdcs_html_menu(self, outertags = '<div id="nav"><ul>%s</ul></div>', tags = '<li><a href="%s">%s</a></li>', navid='default'):
		mnu = ''
		for (href, title, roles, _) in self.nav[navid]:
			if self.inrole(roles):
				mnu += tags % (href, title,)
		return outertags % mnu

	def pdcs_dojo_fading(self):
		self.req.write("""
var hidden = false;
var currentNode = "item_1";
var curInterval = 0;

function fadeInOut() {
		var animArgs = {
				node: currentNode,
				duration: 250
		};

		if (hidden) {
				dojo.fadeIn(animArgs).play();
				hidden = false;
		} else {
				dojo.fadeOut(animArgs).play();
				hidden = true;
		}
}

function startFading(i) {
				currentNode = "item_" + i;
				curInterval = setInterval(fadeInOut, 500);
}

function stopFading(i) {
				currentNode = "item_" + i;
				clearInterval(curInterval);
				if (hidden)
						fadeInOut();
}
		""")

	def pdcs_dojo_grid(self, dbid, qid = 'read', dataurl = '', delim=','):
		if dataurl == '':
			dataurl = self.webfile + '?db=get'
		self.req.write("""
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.data.CsvStore");

dojo.addOnLoad(function() {
	var dataurl = """) 
		self.req.write('"' + dataurl + '"')
		self.req.write(""";
	var storeForGrid = new dojox.data.CsvStore({ url: dataurl, separator: '""" + delim + """'});
		
		var layoutCalls = [[\n""")
		query = self.databases[dbid][qid]
		fields = self.databases[dbid]['fields_' + qid].split(', ')
		fieldswdt = self.databases[dbid]['fields_wdt_' + qid].split(', ')
		hKeys = query[query.find('SELECT') + 7:query.find('FROM') - 1].split(', ')
		for i in range(len(hKeys)):
			self.req.write('\t\t{ field: "' + hKeys[i] + '", name: "' + fields[i] + '", width: ' + fieldswdt[i] + ' }')
			if i != len(hKeys) - 1:
				self.req.write(',\n')
		self.req.write("""]];

		var pageGrid = dijit.byId("grid");
		pageGrid.setStructure(layoutCalls);
		pageGrid.setStore(storeForGrid, {});
		pageGrid.setSortIndex(1, false);
		pageGrid.sort();
		dojo.style(pageGrid, "width", dojo.style(dojo.byId("pageTitle"), "width"));
		pageGrid.sizeChanged();
});
""")

	def pdcs_dojo_auth(self, authe=['Authorization', 'Login:', 'Password:', 'Enter']):
		self.req.write("""
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.Dialog");

var authDlg;
dojo.addOnLoad(function() {
	authDlg = new dijit.Dialog({
		title: '""" + authe[0] + """',
		style: 'width: 300px; text-align: center;'
	});
});
function showAuthDlg() {
		authDlg.attr('content', '<form method="post" action=\"""" + self.webfile + """\"><br />""" + authe[1] + """<br /><input type="text" dojoType="dijit.form.TextBox" name="login" value="anonymous" /><br />""" + authe[2] + """<br /><input type="password" dojoType="dijit.form.TextBox" name="pass" value="" /><br /><br /><button type="submit" dojoType="dijit.form.Button">""" + authe[3] + """</button></form>');
	authDlg.show();
}
""")		
